Python Element’s documentation!¶
Element is a python project created to build websites.
References¶
Install¶
Python Element has been tested with python 2.7 only and might not work with python 3.x.
Short story¶
sudo apt-get install mongodb-server
virtualenv --system-site-packages foobar
source foobar/bin/activate
pip install git+http://github.com/rande/python-element.git
python -m element my-project
cd my-project
python start.py element:demo:fixtures
python start.py tornado:start --verbose -d
Install services¶
The default skeleton required a mongodb server running, this is not mandatory for small website.
apt-get install mongodb-server
Install with virtual env¶
Virtual Env makes sure there are not conflict with any installed lib by creating its own environment.
virtualenv --system-site-packages element
source element/bin/activate
Install python element¶
You can install the package by using the main code on github
pip install git+http://github.com/rande/python-element.git
Setup your first project¶
The following command will help you creating your first project based on a skeleton website that you can tweak to match your needs.
python -m element my-project
The script will explain the next steps.
Architecture¶
Definitions¶
- node: the smallest data available, it represents a content stored into the datastore. An node must contains
- id: the internal identifier used by the datasource
- path: the path to reach the node, the path is the external identifier to the node
- data: a dictionnary of key-value representing the content
- type: the node type, the node type will be used to handle the node
- manager: the manager code which handle this node
- created_at
- published_at
- enabled
- content
- title
- tags
- category
- copyright
- authors
node handler: it is a service used to render a node, there is one service per node type.
Components used¶
- python 2.7: the main python version supported for now
- IoC: it is a dependency container used to handle Element configuration and to instantiate all required services
- Tornado: it is used to handle request and render response, Element also register custom routes to render nodes.
- jinja: render templates.
- unittest2: used to test the framework
- mongodb: the main datastore for the content.
Application bootstrapping¶
The project used IoC to handle configuration, the skeleton application demonstrates some of its usage. The configuration files are stored in the config folder. The configuration is split into several files (this is not mandatory), each files have its own configuration:
- config.yml: this file contains the main configuration: module to load and shared parameters.
- parameters_*.yml: some parameters are only used on some environments, so depends on those parameters the application might behave differently (use different datastore, or webservice’s credentials)
- services.yml: this file can contains your own custom services
Note
This configuration layout is not mandatory, you can organize those files as you want. Just alter the start.py file in order to match your wishes.
There are 2 ways to use the application:
- command line: expose commands to produce or alter data
- web: expose the data to the client.
The command line and the web does not use the same application instance. so make sure every thing is stateless.
Events¶
Most of the code is created using event to increase flexibility with how an user can interact with the Python Element. Some events are explained in the next section, other events are available on the dedicated documentation .
Request / Response workflow¶
- rewrite this part to explain tornado usage
Plugins¶
Every things is a plugin, if you don’t like a feature just don’t enable the plugin and create your own plugin!
You can view current internal plugin in the plugins section
Bower¶
Elements relies on bower to install assets. All plugins use the base path resources/static/vendor to declare assets. So you should/must configure your .bowerrc like this.
{
"directory": "resources/static/vendor"
}
Running Element¶
One Element’s core feature, the render helper, is used to generated sub request processes by a dedicated controller. The internal implementation of this feature rely on an ESI tag or SSI tag. So Element requires to be behind a reverse proxy like Nginx or Varnish.
To run an element instance, you need a process manager like supervisord:
[program:rabaix.net]
command=/home/rabaix/site/bin/python start.py tornado:start -np 8 -p 5001 --bind thomas.rabaix.net
autostart=true
autorestart=true
startsecs=3
user=rabaix
directory=/home/rabaix/site/src
This will spawn 8 independents processes to process http requests.
The Nginx configuration can be:
server {
listen 80;
server_name thomas.rabaix.net;
location / {
ssi on;
proxy_pass http://127.0.0.1:5001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
or the Varnish configuration (from the Symfony2 documentation):
sub vcl_fetch {
/*
Check for ESI acknowledgement
and remove Surrogate-Control header
*/
if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control;
// For Varnish >= 3.0
set beresp.do_esi = true;
// For Varnish < 3.0
// esi;
}
/* By default Varnish ignores Cache-Control: nocache
(https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control),
so in order avoid caching it has to be done explicitly */
if (beresp.http.Pragma ~ "no-cache" ||
beresp.http.Cache-Control ~ "no-cache" ||
beresp.http.Cache-Control ~ "private") {
return (hit_for_pass);
}
}
Note
Depends on the use case, you can use varnish or nginx with specific caching rules, Nginx runs SSI in in parallel while Varnish runs ESI sequentially.
If you don’t have a local reverse proxy (ie, varnish) you can use the proxy.py script which able to render esi:include tag:
[program:rabaix.net]
command=/home/rabaix/site/bin/python proxy.py --bind thomas.rabaix.net -p 5000 -sp 5001
autostart=true
autorestart=true
startsecs=3
user=rabaix
directory=/home/rabaix/site/src
Note
This proxy is not save for production usage. And should only be used to your development environment.
Node¶
It is the smallest data available, it represents a content stored into the datastore. By default, a node is a instance of Node.
NodeContext¶
The NodeHandler is in charge of rendering a Node by using an intermediary object the NodeContext. The NodeContext hold altered the information from the Node, a NodeContext should never be persisted while the Node can. The Node is still available from the NodeContext by using the node attribute.
Change the default Node class¶
It is possible to change the default class Node per node type. (The underlying mechanism change the __class__ property).
In order to do that, you need to create a specific listener to register the class with the type
element.plugins.presentation.listener:
class: element.plugins.presentation.listener.PresentationListener
tags:
event.listener:
- { name: node.mapper.pre_initialize, method: register }
The related python is:
class PresentationListener(object):
def register(self, event):
collection = event.get('meta_collection')
collection.add(Meta(PresentationNode, 'presentation.shower'))
Inside the PresentationNode, you can have your own logic and methods
class PresentationNode(Node):
def __init__(self, uuid=None, data=None):
super(PresentationNode, self).__init__(uuid=uuid, data=data)
def count_slides():
return len(self.data['slides'])
Events¶
handler.request¶
This event is generated by the ioc.extra.tornado.RouterHandler. Events registered:
- element.plugins.security.firewall : this is the security firewall used to control resource access depends on user’s
credentials and depends role required to access to the resource.
handler.response¶
- element.plugins.security.handler.TornadoContextHandler: this is used to store security information into the user’s session
handler.terminate¶
- todo
element.nodes.load.success¶
This event is used when a set of nodes is loaded. While a node is loaded, no element.node.load.success event is notified. This event must be used to alter the raw node data, ie the data altered can be persisted as it.
element.node.load.success¶
This event is used when a node is loaded. (see element.nodes.load.success event)
element.node.load.fail¶
This event is used when a node cannot be found
element.node.pre_delete¶
This event is used when a node will be deleted.
element.node.post_delete¶
This event is used when a node has been deleted.
element.node.pre_save¶
This event is used when a node will be saved.
element.node.post_save¶
This event is used when a node has been saved.
element.node.context.load¶
This event is used to alter the NodeContext object, you should not used this event to alter the node object. You can use this event to alter temporary data (data only required in the Request Scope). The SEO plugins used this event to add missing seo information from the node.
Managers¶
Manager interacts with the datasource to query and store a node. Each manager has its own options and way to query data.
Note
This documentation is under construction, more to come soon
Chain Manager¶
Features¶
- Insert here the different feature available for this plugin
Configuration¶
- Insert the yaml configuration for the DI
element.plugins.cache:
cache_control:
- { "path": "^.*\\.(txt|jpg|png|gif|xls|doc|docx)$", "Cache-Control": ['public', 's-maxage=14212800']}
- { "path": "^(blog|gallery).*", "Cache-Control": ['public', 's-maxage=3600']}
- { "path": "^.*\\.rss", "Cache-Control": ['public', 's-maxage=3600']}
- { "path": "^contact.*", "Cache-Control": ['private', 'must-revalidate']}
- { "path": "^/$", "Cache-Control": ['public', 's-maxage=3600']}
Events¶
- List event or entry points for this plugin
Architecture¶
- Provide information about how the feature is implemented
Filesystem¶
Features¶
- Load contents from a yaml file
Configuration¶
There is no configuration option.
Usage¶
You can create a yaml file with the following structure:
title: Inline Content
type: blog.post
tags: ['red', 'yellow']
----
## my title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean rutrum diam
lectus, eget ultricies purus. Suspendisse pellentesque enim ullamcorper libero
adipiscing vulputate.
## section 1
Curabitur velit ipsum, sagittis volutpat porta at, imperdiet at risus. Donec ipsum nunc,
commodo ut laoreet sed, mollis eu dolor. Praesent iaculis, nisl a laoreet elementum,
odio lacus aliquam risus, et aliquam turpis metus vestibulum dolor.
Maecenas venenatis nulla in metus egestas sollicitudin. Donec convallis sodales
massa, ac feugiat mauris tincidunt vel. Fusce eu leo vel nisi faucibus luctus.
Note
As you notice the file is not a valid yaml file, all the data after the ---- separator will be available in the content field of the node object
Note
This documentation is under construction, more to come soon
MongoDB¶
Features¶
- Insert here the different feature available for this plugin
Configuration¶
- Insert the yaml configuration for the DI
element.plugins.cache:
cache_control:
- { "path": "^.*\\.(txt|jpg|png|gif|xls|doc|docx)$", "Cache-Control": ['public', 's-maxage=14212800']}
- { "path": "^(blog|gallery).*", "Cache-Control": ['public', 's-maxage=3600']}
- { "path": "^.*\\.rss", "Cache-Control": ['public', 's-maxage=3600']}
- { "path": "^contact.*", "Cache-Control": ['private', 'must-revalidate']}
- { "path": "^/$", "Cache-Control": ['public', 's-maxage=3600']}
Events¶
- List event or entry points for this plugin
Architecture¶
- Provide information about how the feature is implemented
Plugins Index¶
Action¶
Features¶
This plugin provides a way to attach Tornado actions from the datasource.
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.action:
Usage¶
An action is composed by:
- name: the route name pointing to the controller, the name can also be used in a template to generate the path.
- path: the relative path pointing to the controller. The final url will be the concatenation of the api.collection node’s path with the path value from the action definition.
- methods: the accepted http method
- default: the default value sent to the controller, the controller must be a service registered into the IOC. The _controller key represents the controller which handled the request object.
Actions Node¶
You can define an action route like this
# labs/pdf.yml
title: PDF generation
type: action.raw
name: wkhtmltopdf_index
methods: ['GET']
defaults:
_controller: element.plugins.wkhtmltopdf.generate:execute
Actions collection¶
In order to attach a set of action, you need to create a node of type action.collection and defines an actions array.
# api.yml
title: API
type: action.collection
actions:
element_api_list_index:
path: /element/node.<_format>
methods: ['GET']
defaults:
_controller: element.api.view.node.list:execute
path: /
The action is a simple service like, where the execute method will be call as configured in the previous file
import json
class CrudView(object):
def execute(self, request_handler, context, *args, **kwargs):
request_handler.set_header('Content-Type', 'application/json')
request_handler.write(json.dumps({"mydata": "myvalue"}))
Once the action is created, you can register it in the IOC like this:
services:
element.api.view.node:
class: element.plugins.api.views.NodeView
Note
For now, actions are registered once when the application is booted.
Redirect Action¶
The plugin also provides a redirect handler, to redirect a node to another one, just create a node like this:
type: action.redirect
redirect: en
Api¶
Features¶
- Expose node from the datasource through a RESTful API.
Configuration¶
You need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.api:
Then, you must have a node api as defined in your datasource:
# api.yml
title: API
type: action.collection
actions:
element_api_node:
path: /element/node/<path:path>.<_format>
methods: ['GET', 'PUT', 'POST', 'DELETE']
defaults:
_controller: element.api.view.node:execute
element_api_list_index:
path: /element/node.<_format>
methods: ['GET']
defaults:
_controller: element.api.view.node.list:execute
path: /
element_api_list:
path: /element/path/<path:path>.<_format>
methods: ['GET']
defaults:
_controller: element.api.view.node.list:execute
element_api_handler_list:
path: /element/handlers.<_format>
methods: ['GET']
defaults:
_controller: element.api.view.handler.list:execute
element_api_handler:
path: /element/handler/<code>.<_format>
methods: ['GET']
defaults:
_controller: element.api.view.handler:execute
Note
The API is not stable.
Usage¶
- GET /api/element/handlers.json : return the list of handlers
- GET /api/element/node.json: return the different node available
- GET /api/element/node/{ID}.json: get a node
- POST /api/element/node/{ID}.json: update a node
- PUT /api/element/node/{ID}.json: create a node
Blog¶
Features¶
- Expose a blog.post node handler
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.blog:
Usage¶
You can create a blog index by creating a node.index node with the following value. The node will list all children of /blog with types = blog.post.
# /blog/_index.yml
title: Posts
type: node.index
filters:
limit: 64
path: /blog
types: [blog.post]
A blog post is defined as:
# /blog/2009/sept/18/are-my-services-coool.yml
type: blog.post
title: Are my services coool ?
format: markdown
enabled: 1
published_at: Fri, 18 Sep 2009 19:19:16
comment_enabled:
----
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras gravida malesuada tellus,
at tincidunt lorem accumsan vel. Vestibulum varius sodales sagittis. Quisque tristique
tempus ligula blandit sodales. Nunc luctus, orci in interdum porttitor, urna massa scelerisque
felis, eget hendrerit sapien eros sed augue. Ut quam mauris, feugiat nec laoreet pellentesque,
molestie eget orci. Vivamus leo leo, convallis et sodales vel, fermentum sed leo. Cras sit
amet dui vel sapien consectetur adipiscing. Pellentesque lectus massa, aliquet et ultrices
sit amet, volutpat vel leo. Nulla aliquet sodales enim ac dictum. Proin mattis arcu a metus
aliquam pulvinar. Phasellus sed lectus elit. Donec vitae urna magna. Vestibulum id volutpat eros.
The format option defines how to handle the content field. You can provide a markdown content or a html content.
Events¶
The default template used is element.plugins.blog:post.html and declare two nodes events that can be used to extends the template.
- node.comment.list: the listener should return the comment list related to the provided node
- node.comment.form: the listener should return the comment form
The element.plugins.disqus plugin can be used to handle comments.
Cache¶
Features¶
- The plugins add support for altering cache information on the response
Configuration¶
You need to enable the plugin by adding the element.plugins.cache module and defines a set of cache_control entries.
element.plugins.cache:
cache_control:
- { "path": "^.*\\.(txt|jpg|png|gif|xls|doc|docx)$", "Cache-Control": ['public', 's-maxage=14212800']}
- { "path": "^(blog|gallery).*", "Cache-Control": ['public', 's-maxage=3600']}
- { "path": "^.*\\.rss", "Cache-Control": ['public', 's-maxage=3600']}
- { "path": "^contact.*", "Cache-Control": ['private', 'must-revalidate']}
- { "path": "^/$", "Cache-Control": ['public', 's-maxage=3600']}
- A cache entry defines:
- path: a regular expression to find the rule that should be applied.
- Cache-Control: the data to append to the response
By default, if no match is found then the Cache-Control value will be private, must-revalidate
Architecture¶
- The plugin listen to the element.node.render_response event.
Contact¶
Features¶
- Add a simple contact form as a block
Configuration¶
You need to enable the module ioc.extra.mailer and configure the smtp settings.
ioc.extra.mailer:
host: smtp.localhost
port:
user:
password:
Usage¶
Create a contact.form node:
# /contact.yml
title: Contact
type: contact.form
email:
to: an-email@localhost
from: 'no-reply@localhost'
subject: 'Contact Form localhost'
Disqus¶
Features¶
- Add a custom block to include disqus comments
Configuration¶
You need to enabled the element.plugins.disqus module.
element.plugins.disqus:
account: account_code
The account value is the name of your account on disqus.
Events¶
- The plugin uses the node.comment.list event to add the comment list.
Errors¶
Features¶
- Return a valid error node
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.errors:
Usage¶
Depends on the error type, the listener will look for - errors/40x node if a node does not exist - errors/50x node if there is an internal error
A node can be anything, here an example:
# /errors/40x.yml
title: Page Not Found
type: page.default
format: markdown
----
The requested URL was not found on the server.
If you entered the URL manually please check your spelling and try again.
Events¶
- The plugin listen to two events: element.node.not_found and element.node.internal_error
Feed¶
Features¶
- Add a handler to render atom/rss feed from query
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.feed:
Usage¶
To create an atom feed, just define a node element.feed.atom
# /feeds/python.atom.yml
title: Python Feeds
type: element.feed.atom
filters:
types: [blog.post]
tags: [python]
To create a RSS feed, just define a node element.feed.rss
# /feeds/python.rss.yml
title: Python Feeds
type: element.feed.rss
filters:
types: [blog.post]
tags: [python]
If you want to create an index of feed, just create a simple index:
# /feeds/_index.yml
title: Feeds List
type: node.index
template: element.plugins.node:index.html
filters:
types: [element.feed.rss, element.feed.atom]
path: /feeds
Livestats¶
Features¶
- Expose information about the CPU and memory usage for the current host
- It requires a websocket connection to work
Configuration¶
You need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.livestats:
websocket:
access: ws://element.vagrant:5000/websocket/system
handler: /websocket/system
Also, you need to expose the livestats template with a simple node.template node
# labs/livestats.yaml
title: System States
type: node.template
template: element.plugins.livestats:system.html
Preview¶

The gauge values
Media¶
- Display a gallery of media
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.media:
Usage¶
You can create a gallery with this node:
# /gallery/2010/australia/_index.yml
title: Australia
type: media.gallery
format: markdown
base_template: element:base_gallery.html
content: |
Australia is one country where everything is possible, from dessert to the sea, from
the walabee to the shark. This country is incredible.
Angular Admin¶
The plugin provides an AngularJS Admin to edit nodes.
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.ngadmin:
Usage¶
You can access to the admin using the url /element/static/element.plugins.ngadmin/index.html. You can create a redirect node to make the url a bit shorter:
# /admin.yml
type: action.redirect
redirect: /element/static/element.plugins.ngadmin/index.html
Note
The ngadmin plugin will be refactored ;)
Node¶
The node plugin is a core plugin as it provides main features to render a node.
Configuration¶
element.plugins.node:
render_type: esi # or ssi
Usage¶
The plugin provide a node index handler to render a list of node
# /blog/_index.yml
title: Feeds List
type: node.index
template: element.plugins.node:index.html
filters:
types: [element.feed.rss, element.feed.atom]
path: /feeds
The filters option accept arguments: - types: filter nodes by types - category: filter by category - path: lookup from the specified path - tags: filter nodes the provided tags - limit: limit the number of result to return - offset: set the offset
Jinja Functions¶
render_node_event¶
This helper allows to create a place holder inside a template where listeners can generate some contents.
A good example is a blog post where comments are required. However, the comment mechanism might not be implemented as many solutions exist. The solution is to used the render_node_event helper to raise a specific event with proper option like the subject.
{{ render_node_event('node.comment.list', options={'subject': context.node}) }}
render¶
This helper render an ESI tag or a SSI tag. This can be useful if you want to reuse a controller. It is a valid solution if you don’t have a node to render.
{{ render(path('element_profiler_wdt', token=token, position='normal')) }}
Jinja Filters¶
markup¶
This filter take a node and return a formatted string.
type: blog.post
title: Test
format: markdown
----
## John Doe
Put a Resume here!!
{{ node|markup }}
Events¶
The plugin listen to different events:
- element.node.load.success and element.nodes.load.success for normalizing a node. The normalization make sure that all required fields are set.
Page¶
Features¶
- Expose a page.default node handler
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.page:
Usage¶
A page node is very similar to a blog node, however it should be used to render simple page.
title: Homepage
type: page.default
format: html
----
<div class="row demo-tiles">
<div class="span3">
<div class="tile">
<img class="tile-image big-illustration" alt="" src="images/illustrations/colors.png" />
<h3 class="tile-title">Blog</h3>
<p>Some posts about <br/>technicals playground.</p>
<a class="btn btn-primary btn-large btn-block" href="blog">Read It</a>
</div>
</div>
<div class="span3">
<div class="tile">
<img class="tile-image" alt="" src="images/illustrations/infinity.png" />
<h3 class="tile-title">Element</h3>
<p>A python CMS based on Tornado with a bit of IOC. </p>
<a class="btn btn-primary btn-large btn-block" href="https://github.com/rande/python-element">Play</a>
</div>
</div>
<div class="span3">
<div class="tile">
<img class="tile-image" alt="" src="images/illustrations/bag.png" />
<h3 class="tile-title">Stats</h3>
<p>Some information about this instance.</p>
<a class="btn btn-primary btn-large btn-block" href="stats/parameters">Get Them</a>
</div>
</div>
<div class="span3">
<div class="tile">
<img class="tile-image" alt="" src="images/illustrations/compass.png" />
<h3 class="tile-title">Resume</h3>
<p>A "standard" <br />curriculum vitae.</p>
<a class="btn btn-primary btn-large btn-block" href="resume">Discover It</a>
</div>
</div>
</div>
The format option defines how to handle the content field. You can provide a markdown content or a html content.
Presentation¶
Features¶
- Expose some presentation nodes (shower, slideshare, raw mode)
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.presentation:
Bower configuration¶
You need to install shower or/and reveal.js, open your bower.json file and add
"dependencies": {
"shower": "~1",
"reveal.js": "2.6.2"
}
Shower Presentation¶
A shower presentation is defined by presentation.shower node type:
# /blog/2009/sept/18/are-my-services-coool.yml
type: presentation.shower
title: How to create a good presentation ?
abstract: Let's try to show off with shower!
published_at: 20 May 2014 12:37:48
theme: ribbon # or bright
----
class: shout
## The presentation
----
## Slide 1
- My bullet point 1
- My bullet point 2
----
## Slide 2
- My bullet point 1
- My bullet point 2
----
### etc ..
...
You can use some metadata to get better support from the theme
- class:
- shout: one big title
- shout grow: a growing big title
- shout shrink: a shrinking bit title
- cover: a title + a picture
id: an html id that you can used to define custom slide
data-timing: a timer before switching to the next slide (value format: 00:03, ie 3s)
You can also include some stylesheet to tweak some slides
class: cover
id: cover
<style>
#cover h2 {
margin: 30px 0px 0px;
color: #FFF;
text-align: center;
font-size: 70px;
}
#cover p {
margin: 10px 0px 0px;
text-align: center;
color: #FFF;
font-style: italic;
font-size: 20px;
}
</style>
## Shower Presentation Engine
Integrated into Python Element
<img src="http://shwr.me/pictures/cover.jpg" />
Reveal.js Presentation¶
A shower presentation is defined by presentation.reveal node type:
# /blog/2009/sept/18/are-my-services-coool.yml
type: presentation.reveal
title: How to create a good presentation ?
published_at: 20 May 2014 12:37:48
----
## The presentation
----
## Slide 1
- My bullet point 1
- My bullet point 2
----
data-state: blackout
## Slide 2
- My bullet point 1
- My bullet point 2
----
### etc ..
...
You can use some metadata to get better support from the theme
- ``data-state`: used to change the background color
Profiler¶
Features¶
- Expose information about the current request
- Memory Usage / Profiling ...
Configuration¶
You need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.profiler:
Also, you need to expose the profiler’s controllers
# profiler.yaml
title: Profiler
type: action.collection
actions:
element_profiler_home:
type: action.raw
path: /<token>
methods: ['GET']
defaults:
_controller: element.plugins.profiler.view:home
element_profiler_view:
type: action.raw
path: /<token>
methods: ['GET']
defaults:
_controller: element.plugins.profiler.view:view
element_profiler_wdt:
type: action.raw
path: /wdt/<token>
methods: ['GET']
defaults:
_controller: element.plugins.profiler.view:wdt
element_profiler_pyinfo:
type: action.raw
path: /pyinfo
methods: ['GET']
defaults:
_controller: element.plugins.profiler.view:pyinfo
element_profiler_import:
type: action.raw
path: /import
methods: ['GET']
defaults:
_controller: element.plugins.profiler.view:import_run
element_profiler_export:
type: action.raw
path: /<token>/export
methods: ['GET']
defaults:
_controller: element.plugins.profiler.view:export_run
element_profiler_purge:
type: action.raw
path: /<token>/purge
methods: ['GET']
defaults:
_controller: element.plugins.profiler.view:purge_run
Usage¶
The feature is enabled on dev mode, it does not work for threaded environment.
Preview¶

The wdt displays memory usage, processing time, controller and python version

Display more detailed information profiling panel: config, request, etc ..
Credits¶
The WDT is a python port of the Symfony2 Profiler. Icons created by Sensio are shared under a Creative Commons Attribution license.
Security¶
Features¶
- Add security and access control to your application
- The current implementation is based on the Security Component from the Symfony2 framework.
Note
For now, there is only one authentication implemented: the http basic.
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.security:
role_hierarchy:
ROLE_PUBLIC: [IS_AUTHENTICATED_ANONYMOUSLY]
ROLE_ADMIN: [ROLE_PUBLIC, ROLE_USER]
providers:
in_memory:
users:
- {'username': 'admin', 'password': 'admin', roles: ['ROLE_ADMIN']}
firewalls:
private:
pattern: ^/(admin|api)(.*)
http_basic:
provider: element.plugins.security.provider.in_memory
# login_path: /admin/login
# use_forward: false
# check_path: /admin/login_check
# failure_path: null
# logout:
# path: /admin/logout
anonymous: false # allow anonymous connection
public:
pattern: "^/.*"
anonymous: true # allow anonymous connection
access_control:
- { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/login-check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/(admin|api), role: ROLE_ADMIN }
- { path: ^/.*, role: ['IS_AUTHENTICATED_ANONYMOUSLY'] }
Seo¶
Features¶
- Add basic support for SEO information (title and metas)
Configuration¶
You need to enable the module element.plugins.seo and configure the settings.
element.plugins.seo:
title_pattern: "%seo.title_pattern%"
metas:
name:
keywords: python, element, cms, markdown
description: Python Element by Thomas Rabaix ~ A CMS based on Tornado with a bit of "ioc"
robots: index, follow
viewport: width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no
property:
# Facebook application settings
#'fb:app_id': XXXXXX
#'fb:admins': admin1, admin2
# Open Graph information
# see http://developers.facebook.com/docs/opengraphprotocol/#types or http://ogp.me/
'og:site_name': Python Element by Thomas Rabaix
'og:description': A CMS based on Tornado
http-equiv:
'Content-Type': text/html; charset=utf-8
#'X-Ua-Compatible': IE=EmulateIE7
Usage¶
You can add seo information on a node, by using a seo fields:
title: Homepage
seo:
title: The Python Element Homepage
metas:
name:
description: Python Element by Thomas Rabaix ~ A CMS based on Tornado with a bit of "ioc" ;)
property:
'og:description': A CMS based on Tornado ;)
type: page.default
format: markdown
----
The content here
The seo information will be rendered by using the special render_node_event helper with the element.seo.headers event.
{% if context %}
{{ render_node_event('element.seo.headers', options={'subject': context}) }}
{% else %}
{{ render_node_event('element.seo.headers') }}
{% endif %}
Note
The seo information from the node will be merged with the one from the default configuration.
Static¶
Features¶
- Add a handler to render static file
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.static:
Usage¶
Just place a store a file in the datasource and access it with a browser. That’s it.
If the url contains the ?mode=preview then the static will be rendered into a preview mode.
Jinja Helpers¶
The plugin provides 2 jinja plugins:
- url_media_resize : take the width as parameter and generates a valid url to a resized version of the targetted media.
- url_media_crop : take the size and the optional crop tuples to generates a valid url to a cropped version of the targetted media.
{% for media in medias %}
<a href="{{ url_media_resize(media, 1440) }}" class="swipebox" title="{{ media.name }}">
<img src="{{ url_media_crop(media, size=(234, 234), crop=(0.5, 0.5)) }}" alt="image" width="250px">
</a>
{% endfor %}
Architecture¶
The plugin provides a StaticNodeLoader to create a node object from a path, the created node type is element.static.
Wkhtmltopdf¶
Features¶
- Add a form to generate a PDF from a provided url using wkhtmltopdf (the software must be available in your path)
Configuration¶
There is no configuration option. You only need to enable the plugin by adding this line into the IoC configuration file.
element.plugins.wkhtmltopdf:
# where the generated pdf will be stored
data_path: %project.root_folder%/data/wkhtmltopdf
Usage¶
Just place a store a file in the datasource and access it with a browser. That’s it.
# pdf.yml
title: PDF generation
type: action.raw
name: wkhtmltopdf_index
methods: ['GET']
defaults:
_controller: element.plugins.wkhtmltopdf.generate:execute
Contributing¶
Thanks for going into this section! This page tries to summaries all information required to contribute to this project. Any contribution must have its own unit tests to ensure good quality and avoid regressions.
Retrieving the code¶
git clone https://github.com/rande/python-element.git
pip install -e python-element
cd python-element
Loading fixtures¶
The repository comes with a set of fixtures that you can load to test the solution, or improve it! To load them just run the following commands:
make fixtures
Unit Tests¶
Unit tests are important to avoid bugs. The current test suite used nosetests, to run tests:
make test
Documentations¶
English is not the first language of programmers, some typos or huge grammatical errors might occur in this project, please feel free to fix them by sending some pull requests.
Of course you are welcome to contributing to the documentation too!